/**
 * https://www.bilibili.com/video/BV1Mb411e7re?p=21
 * LED 点阵屏
 * 1.LED点阵屏由若干个独立的LED组成，LED以矩阵的形式排列，以灯珠亮灭来显示文字、图片、视频等。LED点阵屏广泛应用于各种公共场合，如汽车报站器、广告屏以及公告牌等
 * 2.LED点阵屏分类
 *  按颜色：单色、双色、全彩(RGB)
 *  按像素：8*8、16*16等（大规模的LED点阵通常由很多个小点阵拼接而成）
 *
 * 1.视频的单片机
 *   共8x8个led, 16个引脚
 *   D0 ~D7 : 行的引脚, 连接到 74HC595(移位寄存器, 2根电源+3根数据), 节省IO口
 *   P00~P07: 列的引脚, 连接到MCU的P0口
 *   首先要把 跳线帽 JP595插上 or 不插上 都可(影响的是上方8个单独的led)
 *            跳线帽 JOE 连接右边2个引脚(2,3引脚, 让74HC595的OE端低电平有效)
 *   74HC595:
 *      OE:    output enable, 输出使能, 低电平有效
 *      RCLK:  register clock, 寄存器时钟, 每来1个上升沿, '左侧输入缓存'的数据会被搬运到'右侧输出缓存'
 *      SRCLR: serial clear, 串行数据清0
 *      SRCLK: serial clock, 串行时钟, 每来1个上升沿, SER 的数据就向下移位输入1位到'左侧的输入缓存'
 *      SER:   serial data, 串行数据输入端
 *      QH':   多片级联
 * 
 * 
 * https://www.bilibili.com/video/BV1Mb411e7re?p=22
 * LED点阵屏显示图形&动画
 */
#include <REGX52.H>
#include "Link/_74HC595_Utils.h"
#include "Link/Delay_Utils.h"

//控制 LED 点阵屏 的列, P0 = 0x80;
/* static  */sfr MATRIX_ROW_LED_PORT = 0x80;
// #define MATRIX_ROW_LED_PORT P0  //一样的

void MatrixLED_ShowColumn(unsigned char Column, unsigned char Data, unsigned char isClear);
void showSmile();
void showAnimation();

//动画数组, code: 将数据存储在 ROM 区(值不可更改), 而不是 RAM 区
static code unsigned char Animation_Hello[] = {
    //空屏, 让字符从最右边开始往左移
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    /*--  文字:  H  --*/
    /*--  Fixedsys12;  此字体下对应的点阵为：宽x高=8x16   --*/
    0x00,0x1F,0x1F,0x01,0x01,0x1F,0x1F,0x00,
    0x00,0xF0,0xF0,0x00,0x00,0xF0,0xF0,0x00,
    /*--  文字:  e  --*/
    /*--  Fixedsys12;  此字体下对应的点阵为：宽x高=8x16   --*/
    0x00,0x03,0x07,0x04,0x04,0x07,0x03,0x00,
    0x00,0xE0,0xF0,0x90,0x90,0x90,0x80,0x00,
    /*--  文字:  l  --*/
    /*--  Fixedsys12;  此字体下对应的点阵为：宽x高=8x16   --*/
    0x00,0x10,0x10,0x1F,0x1F,0x00,0x00,0x00,
    0x00,0x10,0x10,0xF0,0xF0,0x10,0x10,0x00,
    /*--  文字:  l  --*/
    /*--  Fixedsys12;  此字体下对应的点阵为：宽x高=8x16   --*/
    0x00,0x10,0x10,0x1F,0x1F,0x00,0x00,0x00,
    0x00,0x10,0x10,0xF0,0xF0,0x10,0x10,0x00,
    /*--  文字:  o  --*/
    /*--  Fixedsys12;  此字体下对应的点阵为：宽x高=8x16   --*/
    0x00,0x03,0x07,0x04,0x04,0x07,0x03,0x00,
    0x00,0xE0,0xF0,0x10,0x10,0xF0,0xE0,0x00,
    /*--  文字:  !  --*/
    /*--  Fixedsys12;  此字体下对应的点阵为：宽x高=8x16   --*/
    0x00,0x00,0x0E,0x1F,0x1F,0x0E,0x00,0x00,
    0x00,0x00,0x00,0xB0,0xB0,0x00,0x00,0x00,
    //空屏, 让字符末尾能移动到最左侧
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};
//动画数组的长度
static unsigned char strLen = 8 * (6 * 2 + 2);

void main_led_matrix() {
    _74HC595_Init();
    while (1) {
        //1.显示笑脸
        // showSmile();
        //2.显示动画
        showAnimation();
    }
}

/**
 * 1.显示笑脸(8*8)
 *   1111   高位D7
 *  1    1 
 * 1 1  1 1
 * 1      1
 * 1 1  1 1
 * 1  11  1
 *  1    1 
 *   1111   低位D0
 * 第1列=0x3C
 */
void showSmile() {
    MatrixLED_ShowColumn(0, 0x3C, 1);
    MatrixLED_ShowColumn(1, 0x42, 1);
    MatrixLED_ShowColumn(2, 0xA9, 1);
    MatrixLED_ShowColumn(3, 0x85, 1);
    MatrixLED_ShowColumn(4, 0x85, 1);
    MatrixLED_ShowColumn(5, 0xA9, 1);
    MatrixLED_ShowColumn(6, 0x42, 1);
    MatrixLED_ShowColumn(7, 0x3C, 1);
}

/**
 * 2.显示动画, 使用"字模提取"软件
 */
static unsigned char i, offSet = 0, scanCount = 0;
void showAnimation() {
    //每次扫描, 只有8列
    for (i = 0; i < 8; i++) {
        MatrixLED_ShowColumn(i, Animation_Hello[i + offSet], 1);
    }
    scanCount ++;
    //每1帧扫描显示10次
    if (scanCount > 10) {
        scanCount = 0;
        //下一帧
        offSet ++;
        if (offSet > strLen - 8) offSet = 0;
        
    }
    
}

/**
 * 点亮 LED 点阵屏 的某一列
 * @param Column 第几列, 0~7
 * @param Data 这1列显示什么数据, 高位在上, 1为亮, 0为灭
 * @param isClear 是否需要延时1ms, 然后位选清0(显示清晰, 消影, 扫描显示)
 */
void MatrixLED_ShowColumn(unsigned char Column, unsigned char Data, unsigned char isClear) {
    //LED 点阵屏 的行
    _74HC595_WriteByte(Data);
    //LED 点阵屏 的哪一列显示
    MATRIX_ROW_LED_PORT = 0x80 >> Column;
    if (isClear) {
        Delayms(1);                 //需要延时(否则显示会比较暗)
        MATRIX_ROW_LED_PORT = 0xFF; //如果不清0, 数据会串到下一位去
    }
}
